//
//  WmcStatefulViewControllerImplementation.swift
//  YPWatermarkCamera
//
//  Created by yupao_ios_macmini06 on 2022/11/1.
//

import Foundation

// MARK: Default Implementation BackingViewProvider

extension WmcBackingViewProvider where Self: UIViewController {
    var backingView: UIView {
        return view
    }
}

extension WmcBackingViewProvider where Self: UIView {
    var backingView: UIView {
        return self
    }
}

// MARK: Default Implementation StatefulViewController

extension WmcStatefulViewController {
    var stateMachine: WmcViewStateMachine {
        return associatedObject(self, key: &stateMachineKey) { [unowned self] in
            return WmcViewStateMachine(view: self.backingView)
        }
    }

    var currentState: WmcStatefulViewControllerState {
        switch stateMachine.currentState {
        case .none: return .content
        case let .view(viewKey): return WmcStatefulViewControllerState(rawValue: viewKey)!
        }
    }

    var lastState: WmcStatefulViewControllerState {
        switch stateMachine.lastState {
        case .none: return .content
        case let .view(viewKey): return WmcStatefulViewControllerState(rawValue: viewKey)!
        }
    }

    // MARK: Views

    var loadingView: UIView? {
        get { return placeholderView(.loading) }
        set { setPlaceholderView(newValue, forState: .loading) }
    }

    var errorView: UIView? {
        get { return placeholderView(.error) }
        set { setPlaceholderView(newValue, forState: .error) }
    }

    var emptyView: UIView? {
        get { return placeholderView(.empty) }
        set { setPlaceholderView(newValue, forState: .empty) }
    }

    // MARK: Transitions

    func setupInitialViewState(_ completion: (() -> Void)? = nil) {
        let isLoading = (lastState == .loading)
        let error: NSError? = (lastState == .error) ? NSError(domain: "com.watermark.WmcStatefulViewController.ErrorDomain", code: -1, userInfo: nil) : nil
        transitionViewStates(loading: isLoading, error: error, animated: false, completion: completion)
    }

    func startLoading(animated: Bool = false, completion: (() -> Void)? = nil) {
        transitionViewStates(loading: true, animated: animated, completion: completion)
    }

    func endLoading(animated: Bool = true, error: Error? = nil, completion: (() -> Void)? = nil) {
        transitionViewStates(loading: false, error: error, animated: animated, completion: completion)
    }

    func transitionViewStates(loading: Bool = false, error: Error? = nil, animated: Bool = true, completion: (() -> Void)? = nil) {
        if hasContent() {
            if let e = error {
                handleErrorWhenContentAvailable(e)
            }
            self.stateMachine.transitionToState(.none, animated: animated, completion: completion)
            return
        }
        var newState: WmcStatefulViewControllerState = .empty
        if loading {
            newState = .loading
        } else if let _ = error {
            newState = .error
        }
        self.stateMachine.transitionToState(.view(newState.rawValue), animated: animated, completion: completion)
    }

    // MARK: Content and error handling

    func hasContent() -> Bool {
        return true
    }

    func handleErrorWhenContentAvailable(_ error: Error) {}

    // MARK: Helper

    fileprivate func placeholderView(_ state: WmcStatefulViewControllerState) -> UIView? {
        return stateMachine[state.rawValue]
    }

    fileprivate func setPlaceholderView(_ view: UIView?, forState state: WmcStatefulViewControllerState) {
        stateMachine[state.rawValue] = view
    }
}

// MARK: Association

private var stateMachineKey: UInt8 = 0

private func associatedObject<T: AnyObject>(_ host: AnyObject, key: UnsafeRawPointer, initial: () -> T) -> T {
    var value = objc_getAssociatedObject(host, key) as? T
    if value == nil {
        value = initial()
        objc_setAssociatedObject(host, key, value, .OBJC_ASSOCIATION_RETAIN)
    }
    return value!
}
